<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>音频倒放工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
        }

        .container {
            background: rgba(255, 255, 255, 0.95);
            backdrop-filter: blur(10px);
            border-radius: 20px;
            padding: 40px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            max-width: 600px;
            width: 100%;
            text-align: center;
        }

        h1 {
            color: #333;
            margin-bottom: 30px;
            font-size: 2.5em;
            background: linear-gradient(45deg, #667eea, #764ba2);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .upload-area {
            border: 3px dashed #667eea;
            border-radius: 15px;
            padding: 40px 20px;
            margin-bottom: 30px;
            transition: all 0.3s ease;
            cursor: pointer;
            background: rgba(102, 126, 234, 0.05);
        }

        .upload-area:hover {
            border-color: #764ba2;
            background: rgba(118, 75, 162, 0.1);
            transform: translateY(-2px);
        }

        .upload-area.dragover {
            border-color: #764ba2;
            background: rgba(118, 75, 162, 0.15);
            transform: scale(1.02);
        }

        .upload-icon {
            font-size: 3em;
            color: #667eea;
            margin-bottom: 15px;
        }

        .upload-text {
            color: #666;
            font-size: 1.2em;
            margin-bottom: 15px;
        }

        .file-input {
            display: none;
        }

        .btn {
            background: linear-gradient(45deg, #667eea, #764ba2);
            color: white;
            border: none;
            padding: 12px 30px;
            border-radius: 25px;
            font-size: 1.1em;
            cursor: pointer;
            transition: all 0.3s ease;
            margin: 10px;
            display: inline-block;
            text-decoration: none;
        }

        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
        }

        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }

        .audio-controls {
            margin: 30px 0;
            padding: 20px;
            background: rgba(102, 126, 234, 0.1);
            border-radius: 15px;
            display: none;
        }

        .audio-player {
            width: 100%;
            margin: 15px 0;
            border-radius: 10px;
        }

        .progress-bar {
            width: 100%;
            height: 6px;
            background: #ddd;
            border-radius: 3px;
            margin: 20px 0;
            overflow: hidden;
        }

        .progress-fill {
            height: 100%;
            background: linear-gradient(45deg, #667eea, #764ba2);
            width: 0%;
            transition: width 0.3s ease;
        }

        .status {
            margin: 20px 0;
            padding: 15px;
            border-radius: 10px;
            display: none;
        }

        .status.success {
            background: rgba(76, 175, 80, 0.1);
            color: #4caf50;
            border: 1px solid rgba(76, 175, 80, 0.3);
        }

        .status.error {
            background: rgba(244, 67, 54, 0.1);
            color: #f44336;
            border: 1px solid rgba(244, 67, 54, 0.3);
        }

        .status.processing {
            background: rgba(255, 193, 7, 0.1);
            color: #ffc107;
            border: 1px solid rgba(255, 193, 7, 0.3);
        }

        .file-info {
            background: rgba(102, 126, 234, 0.1);
            border-radius: 10px;
            padding: 15px;
            margin: 20px 0;
            display: none;
        }

        .spinner {
            border: 3px solid rgba(102, 126, 234, 0.3);
            border-top: 3px solid #667eea;
            border-radius: 50%;
            width: 30px;
            height: 30px;
            animation: spin 1s linear infinite;
            display: inline-block;
            margin-right: 10px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🎵 音频倒放工具</h1>
        
        <div class="upload-area" id="uploadArea">
            <div class="upload-icon">📁</div>
            <div class="upload-text">点击选择音频文件或拖拽到此处</div>
            <div style="color: #999; font-size: 0.9em; margin-top: 10px;">
                支持 MP3、WAV、OGG、M4A 等格式
            </div>
        </div>
        
        <input type="file" id="fileInput" class="file-input" accept="audio/*">
        
        <div class="file-info" id="fileInfo"></div>
        
        <div class="status" id="status"></div>
        
        <div class="progress-bar" id="progressBar" style="display: none;">
            <div class="progress-fill" id="progressFill"></div>
        </div>
        
        <button class="btn" id="reverseBtn" disabled>🔄 开始倒放处理</button>
        
        <div class="audio-controls" id="audioControls">
            <h3 style="margin-bottom: 15px; color: #333;">原始音频</h3>
            <audio class="audio-player" id="originalAudio" controls></audio>
            
            <h3 style="margin: 25px 0 15px; color: #333;">倒放音频</h3>
            <audio class="audio-player" id="reversedAudio" controls></audio>
            
            <a class="btn" id="downloadBtn" style="display: none;">⬇️ 下载倒放音频</a>
        </div>
    </div>

    <script>
        let audioFile = null;
        let audioContext = null;
        let reversedAudioBuffer = null;

        // 获取DOM元素
        const uploadArea = document.getElementById('uploadArea');
        const fileInput = document.getElementById('fileInput');
        const fileInfo = document.getElementById('fileInfo');
        const status = document.getElementById('status');
        const reverseBtn = document.getElementById('reverseBtn');
        const audioControls = document.getElementById('audioControls');
        const originalAudio = document.getElementById('originalAudio');
        const reversedAudio = document.getElementById('reversedAudio');
        const downloadBtn = document.getElementById('downloadBtn');
        const progressBar = document.getElementById('progressBar');
        const progressFill = document.getElementById('progressFill');

        // 初始化音频上下文
        function initAudioContext() {
            if (!audioContext) {
                audioContext = new (window.AudioContext || window.webkitAudioContext)();
            }
        }

        // 显示状态信息
        function showStatus(message, type = 'processing') {
            status.textContent = message;
            status.className = `status ${type}`;
            status.style.display = 'block';
        }

        // 隐藏状态信息
        function hideStatus() {
            status.style.display = 'none';
        }

        // 显示进度条
        function showProgress(percent) {
            progressBar.style.display = 'block';
            progressFill.style.width = percent + '%';
        }

        // 隐藏进度条
        function hideProgress() {
            progressBar.style.display = 'none';
        }

        // 格式化文件大小
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        // 处理文件选择
        function handleFile(file) {
            if (!file.type.startsWith('audio/')) {
                showStatus('请选择有效的音频文件！', 'error');
                return;
            }

            audioFile = file;
            
            // 显示文件信息
            fileInfo.innerHTML = `
                <strong>文件名：</strong>${file.name}<br>
                <strong>文件大小：</strong>${formatFileSize(file.size)}<br>
                <strong>文件类型：</strong>${file.type}
            `;
            fileInfo.style.display = 'block';

            // 启用倒放按钮
            reverseBtn.disabled = false;
            
            // 为原始音频创建URL
            const audioUrl = URL.createObjectURL(file);
            originalAudio.src = audioUrl;
            
            hideStatus();
        }

        // 音频倒放处理
        async function reverseAudio() {
            if (!audioFile) return;

            try {
                initAudioContext();
                showStatus('正在处理音频...', 'processing');
                showProgress(10);

                // 读取音频文件
                const arrayBuffer = await audioFile.arrayBuffer();
                showProgress(30);

                // 解码音频数据
                const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
                showProgress(50);

                // 创建倒放的音频缓冲区
                const sampleRate = audioBuffer.sampleRate;
                const numberOfChannels = audioBuffer.numberOfChannels;
                const length = audioBuffer.length;

                reversedAudioBuffer = audioContext.createBuffer(numberOfChannels, length, sampleRate);
                showProgress(70);

                // 倒放每个声道的数据
                for (let channel = 0; channel < numberOfChannels; channel++) {
                    const inputData = audioBuffer.getChannelData(channel);
                    const outputData = reversedAudioBuffer.getChannelData(channel);
                    
                    // 倒转数组
                    for (let i = 0; i < length; i++) {
                        outputData[i] = inputData[length - 1 - i];
                    }
                }
                showProgress(90);

                // 将倒放的音频缓冲区转换为Blob
                const reversedBlob = await audioBufferToWav(reversedAudioBuffer);
                const reversedUrl = URL.createObjectURL(reversedBlob);
                
                // 设置倒放音频播放器
                reversedAudio.src = reversedUrl;
                
                // 设置下载链接
                downloadBtn.href = reversedUrl;
                downloadBtn.download = `reversed_${audioFile.name.replace(/\.[^/.]+$/, "")}.wav`;
                downloadBtn.style.display = 'inline-block';
                
                // 显示音频控制面板
                audioControls.style.display = 'block';
                
                showProgress(100);
                setTimeout(() => {
                    hideProgress();
                    showStatus('音频倒放处理完成！', 'success');
                    setTimeout(hideStatus, 3000);
                }, 500);

            } catch (error) {
                console.error('处理音频时出错:', error);
                showStatus('处理音频时出错，请尝试其他文件。', 'error');
                hideProgress();
            }
        }

        // 将AudioBuffer转换为WAV格式的Blob
        function audioBufferToWav(buffer) {
            const length = buffer.length;
            const numberOfChannels = buffer.numberOfChannels;
            const sampleRate = buffer.sampleRate;
            const arrayBuffer = new ArrayBuffer(44 + length * numberOfChannels * 2);
            const view = new DataView(arrayBuffer);

            // WAV文件头
            const writeString = (offset, string) => {
                for (let i = 0; i < string.length; i++) {
                    view.setUint8(offset + i, string.charCodeAt(i));
                }
            };

            writeString(0, 'RIFF');
            view.setUint32(4, 36 + length * numberOfChannels * 2, true);
            writeString(8, 'WAVE');
            writeString(12, 'fmt ');
            view.setUint32(16, 16, true);
            view.setUint16(20, 1, true);
            view.setUint16(22, numberOfChannels, true);
            view.setUint32(24, sampleRate, true);
            view.setUint32(28, sampleRate * numberOfChannels * 2, true);
            view.setUint16(32, numberOfChannels * 2, true);
            view.setUint16(34, 16, true);
            writeString(36, 'data');
            view.setUint32(40, length * numberOfChannels * 2, true);

            // 写入音频数据
            let offset = 44;
            for (let i = 0; i < length; i++) {
                for (let channel = 0; channel < numberOfChannels; channel++) {
                    const sample = Math.max(-1, Math.min(1, buffer.getChannelData(channel)[i]));
                    view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true);
                    offset += 2;
                }
            }

            return new Blob([arrayBuffer], { type: 'audio/wav' });
        }

        // 事件监听器
        uploadArea.addEventListener('click', () => fileInput.click());
        
        uploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            uploadArea.classList.add('dragover');
        });

        uploadArea.addEventListener('dragleave', () => {
            uploadArea.classList.remove('dragover');
        });

        uploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            uploadArea.classList.remove('dragover');
            const files = e.dataTransfer.files;
            if (files.length > 0) {
                handleFile(files[0]);
            }
        });

        fileInput.addEventListener('change', (e) => {
            if (e.target.files.length > 0) {
                handleFile(e.target.files[0]);
            }
        });

        reverseBtn.addEventListener('click', reverseAudio);
    </script>
</body>
</html>